﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using ShkSimulation.core.factory;
using ShkSimulation.core.template;
using ShkSimulation.core.util;
using ShkSimulation.desktop.component.tools;
using ShkSimulation.desktop.setting.model;

namespace ShkSimulation.desktop.component {
	
    public sealed partial class NetworkEditControl : UserControl, ResetSimulation {

	    public event Action<int, StationConfigModel> OnStationClick; 
	    
	    public event Action<int, RoadConfigModel> OnRoadClick; 
	    
	    public event Action<int, DemandConfigModel> OnDemandClick; 
	    
	    private Control SelectedControl;
	    
        public NetworkEditControl() {
            InitializeComponent();
            this.Dock = DockStyle.Fill;
            this.SetStyle(ControlStyles.DoubleBuffer, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true);
            this.Resize += (_, _) => { PaintItems(stationData, roadData, demandData); };
        }

        public NetworkEnvironment NetworkInput { get; set; }

        private Dictionary<int/*Id*/, StationConfigModel> stationData;
        private Dictionary<int/*Id*/, RoadConfigModel> roadData;
        private Dictionary<int/*Id*/, DemandConfigModel> demandData;
		private Dictionary<StationConfigModel, StationConfigControl> Stations { get; set; }
		private Dictionary<RoadConfigModel, RoadConfigControl> Roads { get; set; }
		private Dictionary<DemandConfigModel, RoadConfigControl> Demands { get; set; }
		private Dictionary<int/*id*/, int/*index*/> stationIndexInfo;
		private Dictionary<int/*id*/, int/*index*/> roadIndexInfo;
		private Dictionary<DemandConfigModel, int/*index*/> demandIndexInfo;

		public int GetStationIndex(int id) {
			return stationIndexInfo[id];
		}
		
		public int GetRoadIndex(int id) {
			return roadIndexInfo[id];
		}
		
		public int GetDemandIndex(int roadId) {
			return demandIndexInfo[demandData[roadId]];
		}

		public void SetNetworkInputData(DataTable tempStation, DataTable tempRoad, DataTable tempDemand) {
			Dictionary<int/*index*/, CreateParam> stationList = CollectionUtil.DataTableToIndexCreateParams(tempStation);
			Dictionary<int/*index*/, CreateParam> roadList = CollectionUtil.DataTableToIndexCreateParams(tempRoad);
			Dictionary<int/*index*/, CreateParam> demandList = CollectionUtil.DataTableToIndexCreateParams(tempDemand);
			
			Dictionary<int/*index*/, StationConfigModel> station = stationList.ToDictionary(each => each.Key, each => new StationConfigModel(each.Value));
			Dictionary<int/*index*/, RoadConfigModel> road = roadList.ToDictionary(each => each.Key, each => new RoadConfigModel(each.Value));
			Dictionary<int/*index*/, DemandConfigModel> demand = demandList.ToDictionary(each => each.Key, each => new DemandConfigModel(each.Value));

			SetNetworkInputData(station, road, demand);
		}

		public void SetNetworkInputData(Dictionary<int, StationConfigModel> tempStation, 
			Dictionary<int, RoadConfigModel> tempRoad, 
			Dictionary<int, DemandConfigModel> tempDemand) {
			
			ModelValidateCheck.CheckUnique(tempStation.Values, tempRoad.Values);
			Dictionary<int/*id*/, StationConfigModel> stations = tempStation.Values.ToDictionary(each => each.Id);
			Dictionary<int/*id*/, RoadConfigModel> roads = tempRoad.Values.ToDictionary(each => each.Id);
			Dictionary<int/*roadId*/, DemandConfigModel> demands = new Dictionary<int, DemandConfigModel>();
			stationIndexInfo = tempStation.ToDictionary(each => each.Value.Id, each => each.Key);
			roadIndexInfo = tempRoad.ToDictionary(each => each.Value.Id, each => each.Key);
			demandIndexInfo = new Dictionary<DemandConfigModel, int>();
			foreach ((var index, var demandConfigModel) in tempDemand) {
				foreach (var (roadId, roadConfigModel) in roads) {
					if (demandConfigModel.OriginId == roadConfigModel.LastStationId &&
					    demandConfigModel.DestinationId == roadConfigModel.NextStationId) {
						demands[roadId] = demandConfigModel;
						demandIndexInfo[demandConfigModel] = index;
						break;
					}
				}
			}

			stationData = stations;
			roadData = roads;
			demandData = demands;
			StartDraw(stations, roads, demands);
		}

		private void StartDraw(Dictionary<int, StationConfigModel> stations, 
			Dictionary<int, RoadConfigModel> roads, 
			Dictionary<int, DemandConfigModel> demands) {
			this.Invoke(new Action(() => {
				PaintItems(stations, roads, demands);
			}));

		}

		public void Reset() {
			this.Stations = new Dictionary<StationConfigModel, StationConfigControl>();
			this.Roads = new Dictionary<RoadConfigModel, RoadConfigControl>();
			this.Demands = new Dictionary<DemandConfigModel, RoadConfigControl>();
			this.MainControlPanel.Controls.Clear();
		}
		
		private void PaintItems(Dictionary<int, StationConfigModel> stations, 
			Dictionary<int, RoadConfigModel> roads, 
			Dictionary<int, DemandConfigModel> demands) {
			
			Reset();
			if (stations == null || roads == null || demands == null || stations.Count == 0 ||  roads.Count == 0 ||  demands.Count == 0) {
				return;
			}
			var dictionary = PaintStation(new List<StationConfigModel>(stations.Values));
			PaintRoadAndDemand(new List<RoadConfigModel>(roads.Values), demands, dictionary);
			foreach ((StationConfigModel station, StationConfigControl stationControl) in Stations) {
				stationControl.MouseClick += (sender, args) => { StationControlOnMouseClick(station, stationControl); };
				// stationControl.MouseDown += (sender, args) => {
				// 	this.DoDragDrop(station.Id.ToString(), DragDropEffects.Copy);
				// };
			}
			foreach ((RoadConfigModel road, RoadConfigControl roadControl) in Roads) {
				roadControl.MouseClick += (sender, args) => {
					if (args.Button == MouseButtons.Left) {
						RoadControlOnMouseClick(road, roadControl);
					}
				};
			}
			foreach ((DemandConfigModel demandConfigModel, RoadConfigControl roadConfigControl) in Demands) {
				roadConfigControl.OnDemandClick += () => { OnOnDemandClick(demandConfigModel, roadConfigControl); };
			}
		}

		private void OnOnDemandClick(DemandConfigModel demand, RoadConfigControl roadConfigControl) {
			SetSelectedControl(roadConfigControl);
			OnDemandClick?.Invoke(demandIndexInfo[demand], demand);
		}

		private void RoadControlOnMouseClick(RoadConfigModel road, RoadConfigControl roadControl) {
			SetSelectedControl(roadControl);
			OnRoadClick?.Invoke(roadIndexInfo[road.Id], road);
		}

		private void StationControlOnMouseClick(StationConfigModel station, StationConfigControl stationControl) {
			SetSelectedControl(stationControl);
			if (OnStationClick != null) OnStationClick(stationIndexInfo[station.Id], station);
		}
		
		private void SetSelectedControl(Control control) {
			if (control == SelectedControl) {
				return;
			}
			if (SelectedControl != null) {
				if (SelectedControl is RoadConfigControl roadControl) {
					roadControl.ResetColor();
				}else if (SelectedControl is StationConfigControl stationConfigControl) {
					stationConfigControl.ResetColor();
				}
			}
			if (control is RoadConfigControl roadConfigControl) {
				roadConfigControl.RoadColor = Color.Tomato;
				SelectedControl = roadConfigControl;
			}else if (control is StationConfigControl stationConfigControl) {
				stationConfigControl.BackColor = Color.Tomato;
				SelectedControl = stationConfigControl;
			}
		}

		private Dictionary<StationConfigModel, Point> PaintStation(List<StationConfigModel> stations) {
			if (stations == null || stations.Count == 0) {
				return new Dictionary<StationConfigModel, Point>();
			}
			int width = MainControlPanel.Width;
			int height = MainControlPanel.Height;
			Point center = new Point(width / 2, height / 2);
			int size = Math.Min(width, height) / 8;
			int axisLength = Math.Min(width, height) / 2 - size;
			List<Point> points = GetPoints(center, axisLength, stations.Count);
			Dictionary<StationConfigModel, Point> stationDictionary = new Dictionary<StationConfigModel, Point>();
			for (int i = 0; i < points.Count; i++) {
				StationConfigModel station = stations[i];
				StationConfigControl stationControl = new StationConfigControl(station, points[i], Properties.Resources.station, size);
				stationDictionary.Add(station, points[i]);
				((ISupportInitialize)stationControl).EndInit();
				Stations.Add(station, stationControl);
				MainControlPanel.Controls.Add(stationControl);
			}
			return stationDictionary;
		}

		private void PaintRoadAndDemand(List<RoadConfigModel> roads, Dictionary<int, DemandConfigModel> demandConfigModels,
			Dictionary<StationConfigModel, Point> stations) {
			int width = MainControlPanel.Width;
			int height = MainControlPanel.Height;
			int size = Math.Min(width, height) / 8;
			Dictionary<int, StationConfigModel> keyStation = stations.Keys.ToDictionary(each => each.Id, each => each);
			foreach (var road in roads) {
				if (!keyStation.ContainsKey(road.LastStationId) || !keyStation.ContainsKey(road.NextStationId)) {
					Console.WriteLine($@"因为没有对应的上下游{road.LastStationId}: {road.NextStationId}车站，因此不绘制道路:{road.Id}");
					continue;
				}
				StationConfigModel last = keyStation[road.LastStationId];
				StationConfigModel next = keyStation[road.NextStationId];
				DemandConfigModel demandConfigModel = null;
				if (demandConfigModels.ContainsKey(road.Id)) {
					demandConfigModel = demandConfigModels[road.Id];
				}
				RoadConfigControl roadControl = new RoadConfigControl(road, stations[last], stations[next], size, demandConfigModel);
				Roads.Add(road, roadControl);
				if (demandConfigModel != null) {
					Demands.Add(demandConfigModel, roadControl);
				}
				MainControlPanel.Controls.Add(roadControl);

			}
		}

		private List<Point> GetPoints(Point center, int axisLength, int count) {
			List<Point> points = new List<Point>();
			double angle = 2 * Math.PI / count;
			double eachAngle = Math.PI;
			for (int i = 0; i < count; i++) {
				int width = Convert.ToInt32(axisLength * Math.Sin(eachAngle));
				int height = Convert.ToInt32(axisLength * Math.Cos(eachAngle));
				points.Add(new Point(center.X + width, center.Y + height));
				eachAngle += angle;
			}
			return points;
		}

    }
}
